#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
MongoShake 定时同步任务
每5小时执行一次数据同步
"""

import os
import sys
import time
import logging
import subprocess
import signal
from datetime import datetime, timedelta
import json
import requests

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('mongoshake_scheduler.log'),
        logging.StreamHandler(sys.stdout)
    ]
)
logger = logging.getLogger(__name__)

class MongoShakeScheduler:
    def __init__(self, sync_interval_hours=5):
        self.sync_interval_hours = sync_interval_hours
        self.running = True
        self.last_sync_time = None
        self.sync_count = 0
        
        # 设置信号处理
        signal.signal(signal.SIGINT, self.signal_handler)
        signal.signal(signal.SIGTERM, self.signal_handler)
        
        # 加载配置
        self.load_config()
    
    def load_config(self):
        """加载配置文件"""
        try:
            with open('scheduler_config.json', 'r', encoding='utf-8') as f:
                self.config = json.load(f)
        except FileNotFoundError:
            # 默认配置
            self.config = {
                "sync_interval_hours": 5,
                "mongoshake_service": "mongoshake-sync",
                "mongodb_service": "mongodb-instance",
                "health_check_url": "http://localhost:9100",
                "max_retries": 3,
                "retry_delay": 60,
                "notification": {
                    "enabled": False,
                    "webhook_url": "",
                    "email": ""
                }
            }
            self.save_config()
    
    def save_config(self):
        """保存配置文件"""
        with open('scheduler_config.json', 'w', encoding='utf-8') as f:
            json.dump(self.config, f, indent=2, ensure_ascii=False)
    
    def signal_handler(self, signum, frame):
        """信号处理函数"""
        logger.info(f"收到信号 {signum}，正在停止调度器...")
        self.running = False
    
    def check_mongoshake_health(self):
        """检查MongoShake服务健康状态"""
        try:
            response = requests.get(self.config["health_check_url"], timeout=10)
            return response.status_code == 200
        except Exception as e:
            logger.warning(f"健康检查失败: {e}")
            return False
    
    def check_mongodb_health(self):
        """检查MongoDB服务健康状态"""
        try:
            result = subprocess.run(
                ["docker", "exec", self.config["mongodb_service"], "mongosh", "--eval", "db.adminCommand('ping')"],
                capture_output=True,
                text=True,
                timeout=30
            )
            return result.returncode == 0
        except Exception as e:
            logger.warning(f"MongoDB健康检查失败: {e}")
            return False
    
    def start_mongoshake(self):
        """启动MongoShake服务"""
        try:
            logger.info("启动MongoShake服务...")
            result = subprocess.run(
                ["docker-compose", "-f", "docker-compose.mongoshake.yml", "up", "-d"],
                capture_output=True,
                text=True,
                timeout=60
            )
            
            if result.returncode == 0:
                logger.info("MongoShake服务启动成功")
                return True
            else:
                logger.error(f"MongoShake服务启动失败: {result.stderr}")
                return False
        except Exception as e:
            logger.error(f"启动MongoShake服务时出错: {e}")
            return False
    
    def stop_mongoshake(self):
        """停止MongoShake服务"""
        try:
            logger.info("停止MongoShake服务...")
            result = subprocess.run(
                ["docker-compose", "-f", "docker-compose.mongoshake.yml", "down"],
                capture_output=True,
                text=True,
                timeout=60
            )
            
            if result.returncode == 0:
                logger.info("MongoShake服务停止成功")
                return True
            else:
                logger.error(f"MongoShake服务停止失败: {result.stderr}")
                return False
        except Exception as e:
            logger.error(f"停止MongoShake服务时出错: {e}")
            return False
    
    def restart_mongoshake(self):
        """重启MongoShake服务"""
        logger.info("重启MongoShake服务...")
        if self.stop_mongoshake():
            time.sleep(5)  # 等待服务完全停止
            return self.start_mongoshake()
        return False
    
    def perform_sync(self):
        """执行同步操作"""
        logger.info("开始执行数据同步...")
        start_time = datetime.now()
        
        try:
            # 检查MongoDB健康状态
            if not self.check_mongodb_health():
                logger.error("MongoDB服务不健康，跳过本次同步")
                return False
            
            # 重启MongoShake服务以开始新的同步
            if not self.restart_mongoshake():
                logger.error("重启MongoShake服务失败")
                return False
            
            # 等待服务启动
            time.sleep(10)
            
            # 检查MongoShake健康状态
            retry_count = 0
            while retry_count < self.config["max_retries"]:
                if self.check_mongoshake_health():
                    logger.info("MongoShake服务健康检查通过")
                    break
                retry_count += 1
                logger.warning(f"MongoShake健康检查失败，重试 {retry_count}/{self.config['max_retries']}")
                time.sleep(self.config["retry_delay"])
            else:
                logger.error("MongoShake服务健康检查失败，达到最大重试次数")
                return False
            
            # 记录同步完成
            end_time = datetime.now()
            duration = end_time - start_time
            self.last_sync_time = end_time
            self.sync_count += 1
            
            logger.info(f"数据同步完成！耗时: {duration}")
            logger.info(f"同步次数: {self.sync_count}")
            
            # 发送通知
            self.send_notification("同步成功", f"数据同步完成，耗时: {duration}")
            
            return True
            
        except Exception as e:
            logger.error(f"同步过程中出错: {e}")
            self.send_notification("同步失败", f"同步过程中出错: {e}")
            return False
    
    def send_notification(self, title, message):
        """发送通知"""
        if not self.config["notification"]["enabled"]:
            return
        
        try:
            if self.config["notification"]["webhook_url"]:
                # 发送Webhook通知
                payload = {
                    "title": title,
                    "message": message,
                    "timestamp": datetime.now().isoformat(),
                    "sync_count": self.sync_count
                }
                requests.post(
                    self.config["notification"]["webhook_url"],
                    json=payload,
                    timeout=10
                )
                logger.info("Webhook通知发送成功")
            
            # 这里可以添加邮件通知等其他通知方式
            
        except Exception as e:
            logger.warning(f"发送通知失败: {e}")
    
    def get_next_sync_time(self):
        """计算下次同步时间"""
        if self.last_sync_time:
            return self.last_sync_time + timedelta(hours=self.sync_interval_hours)
        else:
            return datetime.now() + timedelta(hours=self.sync_interval_hours)
    
    def run(self):
        """运行调度器"""
        logger.info(f"MongoShake调度器启动，同步间隔: {self.sync_interval_hours}小时")
        logger.info(f"下次同步时间: {self.get_next_sync_time()}")
        
        while self.running:
            try:
                current_time = datetime.now()
                next_sync_time = self.get_next_sync_time()
                
                # 检查是否到达同步时间
                if current_time >= next_sync_time:
                    logger.info("到达同步时间，开始执行同步...")
                    self.perform_sync()
                    logger.info(f"下次同步时间: {self.get_next_sync_time()}")
                else:
                    # 计算等待时间
                    wait_seconds = (next_sync_time - current_time).total_seconds()
                    logger.info(f"等待下次同步，剩余时间: {wait_seconds/3600:.1f}小时")
                    
                    # 每10分钟检查一次
                    check_interval = min(600, wait_seconds)
                    time.sleep(check_interval)
                
            except KeyboardInterrupt:
                logger.info("收到中断信号，正在停止...")
                break
            except Exception as e:
                logger.error(f"调度器运行出错: {e}")
                time.sleep(60)  # 出错后等待1分钟再继续
        
        logger.info("MongoShake调度器已停止")

def main():
    """主函数"""
    import argparse
    
    parser = argparse.ArgumentParser(description='MongoShake定时同步调度器')
    parser.add_argument('--interval', type=int, default=5, 
                       help='同步间隔（小时），默认5小时')
    parser.add_argument('--config', type=str, default='scheduler_config.json',
                       help='配置文件路径')
    parser.add_argument('--daemon', action='store_true',
                       help='以守护进程模式运行')
    
    args = parser.parse_args()
    
    # 创建调度器
    scheduler = MongoShakeScheduler(sync_interval_hours=args.interval)
    
    if args.daemon:
        # 守护进程模式
        import daemon
        with daemon.DaemonContext():
            scheduler.run()
    else:
        # 前台模式
        scheduler.run()

if __name__ == "__main__":
    main() 